package decoder;

import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.ArrayList;

import arithmetic.ArithmeticModel;
import arithmetic.BitInputStream;


public class ArithmeticDecoder 
{
	private ArithmeticModel model;
	private String fPath;
	private int range;
	private int upper;
	private int lower = 0;
	
	boolean isFinished = false;
	
	public ArithmeticDecoder(String msg) throws IOException{
		this.model = new ArithmeticModel();
		this.range = model.getRange();
		this.upper = range - 1;
		
		decode(msg);
	}
	
	public ArithmeticDecoder(FileInputStream instream,String filePath) throws IOException{
		this.model = new ArithmeticModel();
		this.range = model.getRange();
		this.upper = range - 1;
		this.fPath=filePath;
		
		StringBuilder sb = new StringBuilder();
		BitInputStream bitIn = new BitInputStream(instream);
		
		int b;
		while((b = (int) bitIn.read(1)) != -1){
			sb.append(String.valueOf(b));
		}
		
		decode(sb.toString());
	}
	
	public void decode(String msg) throws IOException{
		BufferedWriter fileOut = new BufferedWriter( new OutputStreamWriter(new FileOutputStream(fPath+"decompressed.txt")));
		
		ArrayList<Character> outputArray = new ArrayList<>();
		
		int m = model.getBitLength();	//length of bit word
		int i = 0;
		int tag = Integer.parseInt(msg.substring(i, i+m), 2);
	
		try{
			while(!isFinished){
				int coef = upper - lower +1;
				int t = (int)Math.floor((tag-lower+1) * ((double)model.getTotalCount() / coef) -1/(coef));
				char ch = model.getCharByTag(t);
				if(ch == '\0'){
					isFinished = true;
				}
				else{
					outputArray.add(ch);
					fileOut.write(ch);
					upper = lower + (int)Math.floor(coef * ((double)model.getLetterCumCount(ch) / model.getTotalCount())) - 1;
					lower = lower + (int)Math.floor(coef * ((double)model.getPreviousLetterCumCount(ch) / model.getTotalCount()));
					
					while(true){
						if(lower >= range/2){
							upper = 2*upper - range + 1;
							lower = 2*lower - range;
							
							tag = Integer.parseInt(msg.substring(++i, i+m), 2);
							
						}
						else if(upper < range/2){
							upper = 2*upper + 1;
							lower = 2*lower;
							
							tag = Integer.parseInt(msg.substring(++i, i+m), 2);
							
						}
						else if(lower >= range/4 && upper < 3*range/4){
							upper = 2*upper - range/2 + 1;
							lower = 2*lower - range/2;
							tag = Integer.parseInt(msg.substring(++i, i+m), 2);
							if(tag >= range/2)
								tag = tag - range/2;
							else
								tag = tag + range/2;
						}
						else
							break;
					}
				}
				
			}
		}
		catch(StringIndexOutOfBoundsException ex){}
		
		fileOut.close();
	}
	
}
